// Kanrong
#ifndef __H_DIRECTORY__
#define __H_DIRECTORY__

#include "tstring.h"
#include "Path.h"
#include "file.h"
#include <shlwapi.h>
#include <shellapi.h>
#include <vector>

#ifdef WIN32
#pragma warning(push)
#pragma warning(disable : 4996)
#endif

namespace bl{


class Directory
{
public:
	static void CreateDirectory(const tstring& path)
	{
		tstring parent = Path(path).GetParent();
		if(!parent.empty())
		{
			if(!Exist(parent))
				CreateDirectory(parent);
		}
		::CreateDirectory(path.c_str(), NULL);
	}

	static bool Exist(const tstring& path)
	{
		DWORD att = ::GetFileAttributes(path.c_str());
		if(att == INVALID_FILE_ATTRIBUTES)
		{
			//if(::GetLastError() == ERROR_FILE_NOT_FOUND)
				return false;
		}

		// Not a directory
		if( !(att&FILE_ATTRIBUTE_DIRECTORY) )
			return false;

		return true;
	}

	static void Delete(const tstring& path)
	{
		if(!Exist(path))
			return;

		// Returns true if this directory name is 'safe' for deletion (doesn't contain
		// "..", doesn't specify a drive root)
		bool ok = false;
		for(tstring::const_iterator it = path.begin(); it!=path.end(); it++)
		{
			tstring::value_type s = *it;
			if (s != _T('\\') && s != _T('/') && s != _T(':') && s != _T('.')) {
				ok = true;
			}
			if (s == _T('.') && it>path.begin() && *(it-1) == _T('.')) {
				ok = false;
				break;
			}
			if (s == _T(':')) {
				ok = false;
			}
		}
		if(!ok)
			return;

		WIN32_FIND_DATA find_data = {0};
		tstring findDir = Path(path).Combine(_T("*")).ToString();

		HANDLE hFind = FindFirstFile(findDir.c_str(), &find_data);
		if(hFind != INVALID_HANDLE_VALUE)
		{
			do 
			{
				if(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
				{
					if(StrCmpNIC(find_data.cFileName, _T("."), 2) == 0 ||
						StrCmpNIC(find_data.cFileName, _T(".."), 3) == 0)
						continue;

					tstring subDir = Path(path).Combine(find_data.cFileName).ToString();
					Delete(subDir);
				}
				else
				{
					//File::Delete(Path(path).Combine(find_data.cFileName));
					::DeleteFile(Path(path).Combine(find_data.cFileName));
				}
			} while (::FindNextFile(hFind, &find_data));

			::FindClose(hFind);
		}

		::RemoveDirectory(path.c_str());
	}

	static void GetFiles(const tstring& path, std::vector<tstring>* files)
	{
		GetFiles(path, _T("*"), files);
	}

	static void GetFiles(const tstring& path, const tstring& wildcard,  std::vector<tstring>* files)
	{
		files->clear();
		if(!Exist(path))
			return;

		WIN32_FIND_DATA find_data = {0};
		tstring findDir = Path(path).Combine(wildcard).ToString();

		HANDLE hFind = FindFirstFile(findDir.c_str(), &find_data);
		if(hFind != INVALID_HANDLE_VALUE)
		{
			do
			{
				if(find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || !(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
				{
					files->push_back(find_data.cFileName);
				}
			}
			while(::FindNextFile(hFind, &find_data));
			::FindClose(hFind);
		}
	}

	// "X:\" "X:"
	static bool IsRoot(const tstring& path)
	{
		tstring::size_type pos = path.find(_T(':'));
		if(pos == tstring::npos)
			return false;

		tstring::size_type sepPos = path.find(_T('\\'), pos);
		if (sepPos == tstring::npos || sepPos==path.length()-1)
			return true;

		return false;
	}

	static bool CopyDirectory(tstring from, tstring to)
	{
		tchar szFrom[MAX_PATH + 1] = {0};
		tchar szTo[MAX_PATH + 1] = {0};
		_tcsncpy(szFrom, from.c_str(), MAX_PATH);
		_tcsncpy(szTo, to.c_str(), MAX_PATH);

		SHFILEOPSTRUCT fileOp = {0};
		fileOp.hwnd = NULL;
		fileOp.wFunc = FO_COPY;
		fileOp.pFrom = szFrom;
		fileOp.pTo = szTo;
		fileOp.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT;

		return 0 == SHFileOperation(&fileOp);
	}

	static bool MoveDirectory(tstring from, tstring to)
	{
		tchar szFrom[MAX_PATH + 1] = {0};
		tchar szTo[MAX_PATH + 1] = {0};
		_tcsncpy(szFrom, from.c_str(), MAX_PATH);
		_tcsncpy(szTo, to.c_str(), MAX_PATH);

		SHFILEOPSTRUCT fileOp = {0};
		fileOp.hwnd = NULL;
		fileOp.wFunc = FO_MOVE;
		fileOp.pFrom = szFrom;
		fileOp.pTo = szTo;
		fileOp.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT;

		return 0 == SHFileOperation(&fileOp);
	}


};



}


#ifdef WIN32
#pragma warning(pop)
#endif


#endif